package com.example.demo.util;

import com.jcraft.jsch.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShellUtil {

    private static final Map<String, ShellSession> SHELL_SESSION_MAP    = new HashMap<>();//sessionMap
    private static final JSch                      jsch                 = new JSch();//session构造器
    private static final Integer                   TIME_OUT_HOUR        = 1;//超时时间
    private static final Integer                   DEFAULT_PORT         = 22;//默认连接端口
    private static final String                    DEFAULT_CHANNEL_TYPE = "exec"; //默认类别

    private static ExecutorService executorQueue = Executors.newSingleThreadExecutor();


    public static void main(String[] args) {
        String root = ShellUtil.shellCommand("192.168.159.128", "root", "123456", "nohup java -jar /opt/tmp/demo-web-1.0-SNAPSHOT.jar & ");
        System.out.println(root);
    }

    public static void runExecute(Runnable paramRunnable) {
        executorQueue.submit(paramRunnable);
    }

    public static String shellCommand(final String address, final String username, final String password, final String command) {
        return ShellUtil.shellCommand(address, username, password, ShellUtil.DEFAULT_CHANNEL_TYPE, command);
    }

    public static String shellCommand(final String address, final String username, final String password,
                                      final String channelType, final String command) {
        return ShellUtil.shellCommand(address, username, password, ShellUtil.DEFAULT_PORT, channelType, command);
    }


    public static String shellCommand(final String address, final String username, final String password,
                                      final Integer port, final String channelType, final String command) {
        if (!ShellUtil.checkNotNull(address, username, password, port, channelType, command))
            throw new NullPointerException("params cannot be null");

        if (!ShellUtil.SHELL_SESSION_MAP.containsKey(address + ":" + username))
            ShellUtil.SHELL_SESSION_MAP.put(address + ":" + username, new ShellUtil.ShellSession(address, username, password, port));

        ShellSession shellSession = ShellUtil.SHELL_SESSION_MAP.get(address + ":" + username);
        shellSession.updateLastExecTime();//更新操作时间

        //清除超时session实例
        ShellUtil.SHELL_SESSION_MAP.entrySet().removeIf(entry -> entry.getValue().lastExecTime.getTime() + ShellUtil.TIME_OUT_HOUR * 60 * 60 * 1000 < System.currentTimeMillis());
        return shellSession.execute(channelType, command);

    }

    private static Boolean checkNotNull(Object... objects) {
        if (objects == null)
            return false;
        for (Object obj : objects) {
            if (obj == null)
                return false;
        }
        return true;
    }

    static class ShellSession {
        Session session;
        Date    lastExecTime;

        private ShellSession(String address, String username, String password, Integer port) {
            try {
                session = ShellUtil.jsch.getSession(username, address, port);
                session.setPassword(password);
                session.setConfig("StrictHostKeyChecking", "no");//去掉连接确认的
                session.connect(30000);
            } catch (JSchException e) {
                e.printStackTrace();
            }
        }

        private void updateLastExecTime() {
            lastExecTime = Calendar.getInstance().getTime();
        }

        private String execute(String channelType, String command) {
            if (this.session == null)
                return null;
            Channel channel = null;
            //InputStream input = null;
            BufferedReader bufferedReader = null;
            StringBuilder  resp           = new StringBuilder();
            try {
                channel = this.session.openChannel(channelType);
                ((ChannelExec) channel).setCommand(command);

                channel.setInputStream(null);
                ((ChannelExec) channel).setErrStream(System.err);
                channel.connect();

                bufferedReader = new BufferedReader(new InputStreamReader(channel.getInputStream()));

                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    resp.append(line).append("\n");
                }
                if (!resp.toString().equals("")) {
                    resp = new StringBuilder(resp.substring(0, resp.length() - 1));
                }
            } catch (JSchException | IOException e) {
                e.printStackTrace();
            } finally {
                if (bufferedReader != null) {
                    try {
                        bufferedReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (channel != null) {
                    channel.disconnect();
                }
            }
            return resp.toString();
        }
    }
}
